﻿#include "../Inc/qlua.h"

QLua::QLua()
{
    // 初始化程序路径
    QString path = QCoreApplication::applicationDirPath();
    path.append("/luaScripts");     // 脚本存放路径
    QDir dir(path);
    // 不存在就创建一个
    if(!dir.exists())
    {
        dir.mkdir(path);
    }
    // 路径下的脚本
    path.append("/command.lua");
    QFile file(path);
    // 不存在就创建一个脚本
    if(!file.exists())
    {
        if(file.open(QIODevice::WriteOnly))
        {
            file.write("");
        }
        file.close();
    }
    // 最后设置脚本路径
    this->setScriptFileName(path);

    m_builtInfunction =  {"next", "xpcall", "rawset", "collectgarbage",
           "rawget", "print", "pairs", "warn", "select", "require",
            "loadfile", "load", "pcall", "type", "ipairs", "getmetatable",
             "tostring", "assert", "tonumber", "error", "rawequal",
               "setmetatable", "dofile", "rawlen"};
}

QLua::~QLua()
{
    if(L != nullptr)
    {
        L = nullptr;
    }
}

QString QLua::GetCmd(QStringList list)
{
    QString sRet = "";
    //创建Lua状态机
    L = luaL_newstate();
    //开启常用库
    luaL_openlibs(L);

    //加载脚本,字符串转宽字符，使用toLocal8Bit()，不要用toLatin1(),否则不支持中文路径
    if(luaL_dofile(L, m_ScriptFileName.toLocal8Bit().data()) == LUA_OK)
    {
        if(!list.isEmpty())
        {
            lua_newtable(L);      // 创建一个新 table 并压入栈
            for(int i = 1; i <= list.length(); i++)
            {
                lua_pushnumber(L, i);
                lua_pushstring(L, list.at(i - 1).toLatin1().data());
                lua_settable(L, -3);
            }
            //获取脚本方法
            lua_getglobal(L, "GetCmd");
            // 将table内容压栈
            lua_pushvalue(L, -2);
            if(!lua_isnil(L, -1))
            {
                lua_pcall(L, 1, 1, 0);
                if(lua_isstring(L, -1))
                {
                    sRet = lua_tostring(L, -1);
                }
            }
            else
            {
                sRet = "Lua方法获取失败";
            }
        }
        else
        {
            sRet = "列表空内容";
        }
    }
    else
    {
        sRet = "脚本加载失败";
    }

    //清理栈顶
    lua_pop(L, 1);
    //关闭Lua状态机
    lua_close(L);
    //指针归零
    L = nullptr;
    return sRet;
}

QString QLua::GetPrintLabel(QString msg, int mark)
{
    QString sRet = "";
    // 创建Lua状态机
    L = luaL_newstate();
    // 开启常用库，以便Lua脚本可以使用它们
    luaL_openlibs(L);

    // 加载脚本，字符串转宽字符，使用toLocal8Bit()，不要用toLatin1()，否则不支持中文路径
    if(luaL_dofile(L, m_ScriptFileName.toLocal8Bit().data()) == LUA_OK)
    {
        // 获取脚本方法
        lua_getglobal(L, "GetPrintLabel");

        // 检查全局表中是否存在方法
        if(!lua_isnil(L, -1))
        {
            // 传达参数压栈
            lua_pushstring(L, msg.toUtf8().data());
            lua_pushnumber(L, mark);
            // 执行方法
            lua_pcall(L, 2, 1, 0);

            // 检查栈顶是否为字符串类型
            if(lua_isstring(L, -1))
            {
                // 获取结果
                sRet = lua_tostring(L, -1);
            }
        }
        else
        {
            // 如果Lua方法获取失败，设置返回值为错误信息
            sRet = "Lua方法获取失败";
        }
    }
    else
    {
        // 如果脚本加载失败，设置返回值为错误信息
        sRet = "脚本加载失败";
    }

    // 清理栈顶
    lua_pop(L, 1);
    // 关闭Lua状态机
    lua_close(L);
    // 指针归零
    L = nullptr;
    // 返回结果字符串
    return sRet;
}

QStringList QLua::getAllFunction(bool flag)
{
    QStringList funcList;
    L = luaL_newstate();
    if (!L) return funcList;

    luaL_openlibs(L);

    if (luaL_dofile(L, m_ScriptFileName.toLocal8Bit().data()) == LUA_OK) {
        lua_getglobal(L, "_G");
        lua_pushnil(L);

        while (lua_next(L, -2)) {
            if (lua_isfunction(L, -1)) {
                if (const char* name = lua_tostring(L, -2)) {
                    funcList << QString::fromUtf8(name);
                }
            }
            lua_pop(L, 1);
        }
        lua_pop(L, 1);  // 仅移除_G表
    } else {
        funcList << "Lua load error:" << lua_tostring(L, -1);
        lua_pop(L, 1);
    }

    lua_close(L);
    if(flag)
    {
        funcList = this->diffQStringList(m_builtInfunction,funcList);
    }
    return funcList;
}

QString QLua::ScriptFileName() const
{
    return m_ScriptFileName;
}

void QLua::setScriptFileName(const QString &newScriptFileName)
{
    m_ScriptFileName = newScriptFileName;
}

QStringList QLua::diffQStringList(QStringList list1, QStringList list2)
{
    // 找出差异
    QSet<QString> set1(list1.begin(), list1.end());
    QSet<QString> set2(list2.begin(), list2.end());
    QStringList differences = (set1 - set2 + set2 - set1).values();
    return differences;
}

/*
 * QList("next", "xpcall", "rawset", "collectgarbage",
 *  "rawget", "print", "pairs", "warn", "select", "require",
 *   "loadfile", "load", "pcall", "type", "ipairs", "getmetatable",
 *    "tostring", "assert", "tonumber", "error", "rawequal",
 *    "setmetatable", "dofile", "rawlen")
内置函数识别依据：
基础库函数：print/pairs/ipairs等属于Lua基础库标准函数
元表操作：getmetatable/setmetatable是元表核心操作
类型转换：tonumber/tostring是基础类型转换函数
错误处理：xpcall/pcall/error/assert属于错误处理机制
表操作：rawget/rawset/rawequal是原始表访问函数
模块加载：require/loadfile/dofile是模块系统组件
其他标准库：collectgarbage/select等属于标准库范畴
*/
